package com.mazaiting.classfile;

import cn.hutool.core.util.ObjectUtil;
import com.mazaiting.classfile.attr.MemberInfo;
import com.mazaiting.classfile.attr.info.AttributeInfo;
import com.mazaiting.classfile.constant.ConstantPool;
import com.mazaiting.rtda.heap.MtAccessFlags;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

/**
 * 类文件结构
 * 官方文档: https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
 *
 * @author mazaiting
 * @since 2021/8/11
 */
@Slf4j
@Getter
public class ClassFile {

    /**
     * 魔数
     */
    private static final String MAGIC_NUMBER = "CAFEBABE";

    /**
     * 类读取工具
     */
    private ClassReader reader;

    /**
     * 二进制数据
     */
    private final byte[] data;

    /**
     * 魔数
     * 魔数只有为CAFEBABE时，才代表是 class 文件
     */
    private String magic;
    /**
     * 主要版本 无符号u2
     */
    private int majorVersion;
    /**
     * 小版本 无符号u2
     */
    private int minorVersion;
    /**
     * 常量池
     */
    private ConstantPool constantPool;
    /**
     * 访问标识符 u2
     */
    private short accessFlags;
    /**
     * 类索引 u2
     */
    private short thisClass;
    /**
     * 超类索引 u2
     */
    private short superClass;
    /**
     * 接口索引数组 无符号u2数组
     */
    private int[] interfaces;
    /**
     * 字段数组
     */
    private MemberInfo[] fields;
    /**
     * 方法数组
     */
    private MemberInfo[] methods;
    /**
     * 属性数组
     */
    private AttributeInfo[] attributes;

    public ClassFile(byte[] data) {
        this.data = data;
    }

    /**
     * 读取内容
     */
    public void read() {
        reader = new ClassReader(data);
        // 读取并校验魔数
        readAndCheckMagic();
        // 读取并校验版本
        readAndCheckVersion();
        // 读取常量池
        this.constantPool = readConstantPool();
        // 读取访问标识符
        this.accessFlags = reader.readU2ToShort();
        // 读取当前类索引
        this.thisClass = reader.readU2ToShort();
        // 读取超类索引
        this.superClass = reader.readU2ToShort();
        // 读取接口数组
        this.interfaces = reader.readU2UnsignedShorts();
        // 读取字段数组
        this.fields = MemberInfo.readMembers(reader, constantPool);
        // 读取方法数组
        this.methods = MemberInfo.readMembers(reader, constantPool);
        // 读取属性数组
        this.attributes = MemberInfo.readAttributes(reader, constantPool);
    }

    /**
     * 读取并校验魔数
     */
    private void readAndCheckMagic() {
        this.magic = reader.readString(4);

        if (ObjectUtil.notEqual(this.magic, MAGIC_NUMBER)) {
            throw new RuntimeException("java.lang.ClassFormatError: magic!");
        }
    }

    /**
     * 读取并校验版本
     */
    private void readAndCheckVersion() {
        this.minorVersion = reader.readU2ToUnsignedShort();
        this.majorVersion = reader.readU2ToUnsignedShort();
        switch (this.majorVersion) {
            case 45:
                return;
            case 46:
            case 47:
            case 48:
            case 49:
            case 50:
            case 51:// 7.0
            case 52:// 8.0
                if (this.minorVersion == 0) {
                    return;
                }
            default:
                throw new RuntimeException("java.lang.UnsupportedClassVersionError!");
        }

    }

    /**
     * 解析常量池
     */
    private ConstantPool readConstantPool() {
        int count = reader.readU2ToUnsignedShort();
        log.debug("共有常量: {}", count);
        ConstantPool constantPool = new ConstantPool(count);
        constantPool.read(reader);
        return constantPool;
    }

    /**
     * 获取访问标识符
     * @return 访问标识字符串
     */
    public String getAccessFlagsStr() {
        return MtAccessFlags.getClassFlags(this.accessFlags);
    }

    /**
     * 获取类名
     * @return 类名
     */
    public String getClassName() {
        return this.constantPool.getClassName(this.thisClass);
    }

    /**
     * 获取超类类名
     * 只有 java.lang.Object没有超类
     * @return 超类类名
     */
    public String getSuperClassName() {
        if (this.superClass > 0) {
            return this.constantPool.getClassName(this.superClass);
        }
        return "";
    }

    /**
     * 获取接口名数组
     * @return 接口名数组
     */
    public String[] getInterfaceNames() {
        String[] strings = new String[this.interfaces.length];
        for (int i = 0; i < this.interfaces.length; i++) {
            strings[i] = this.constantPool.getClassName(this.interfaces[i]);
        }
        return strings;
    }

    /**
     * 解析类
     * @param data 二进制数据
     * @return 类文件
     */
    public static ClassFile parse(byte[] data) {
        ClassFile classFile = new ClassFile(data);
        classFile.read();
        return classFile;
    }

}


















